<?php

namespace App\Http\Middleware;

// use App\Commons\Domain\Module\Account\Entity\TokenEntity;
use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\PayloadException;
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Exceptions\UserNotDefinedException;

/**
 * 自定义JWT-auth认证中间件
 */
class ExJWTAuth
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next, string ...$guards)
    {
        $auth      = null;
        $use_guard = null;
        $guards    = empty($guards) ? [auth()->getDefaultDriver()] : $guards;

        try {
            foreach ($guards as $guard) {
                if (! config('auth.guards.'.$guard)) {
                    continue;
                }
                $use_guard = $guard;
                $auth      = auth($use_guard);
                $auth->setRequest($request);
                $auth->parseToken()->getPayload();

                if ($auth->user()) {
                    // 设置当前Guard
                    auth()->shouldUse($use_guard);

                    return $next($request);
                }
            }
            // TODO: 是否需要把token存入数据库中，从后台失效token，增加从后台查询token的合法性
            throw_e(0xf00012);
            // TODO: 增加对不同身份用户的认证，如管理员和普通用户
        } catch (TokenExpiredException $e) {
            // throw_e(0xf00002);
            // 增加token过期，自动刷新的机制
            $refreshToken = $this->_getRefreshToken($auth);
            auth()->shouldUse($use_guard);
            // 设置当前请求的token，否则本次请求无效
            $request->headers->set('Authorization', 'Bearer '.$refreshToken);
            foreach ($guards as $guard) {
                if (! config('auth.guards.'.$guard)) {
                    continue;
                }
                $auth = auth($guard);
                $auth->setRequest($request);
                $auth->parseToken()->getPayload();
                if ($auth->user()) {
                    // 设置当前Guard
                    auth()->shouldUse($guard);
                }
            }
            $response = $next($request);

            // Send the refreshed token back to the client.
            $response->headers->set('Authorization', 'Bearer '.$refreshToken);

            return $response;
        } catch (TokenInvalidException | TokenBlacklistedException $e) {
            throw_e(0xf00022);
        } catch (JWTException $e) {
            throw_e(0xf00032);
        }

        return $next($request);
    }

    private function _getRefreshToken($auth)
    {
        $token = $auth->getToken()->get();

        return cache()->lock($token, 3)
            ->block(
                3,
                function () use ($auth, $token) {
                    $refreshToken = cache("token_gracelist:{$token}");
                    if ($refreshToken) {
                        return $refreshToken;
                    }

                    try {
                        $refreshToken = $auth->refresh();
                        cache(["token_gracelist:{$token}" => $refreshToken], 300);
                        // 刷新token后，删除原来的token
                        // app(TokenEntity::class)->refreshToken($token, $refreshToken);
                    } catch (JWTException $e) {
                        report($e);
                        throw_e(0xf00002);
                    }

                    return $refreshToken;
                }
            );
    }
}
